﻿using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;

namespace Community.SharePoint.FAST.PowerShell
{
    internal class DirectoryHelper
    {
        private static int SYMLINK_FLAG_DIRECTORY = 1;
        private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
        private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

        [DllImport("kernel32.dll")]
        static extern bool CreateSymbolicLink(string symlinkFileName, string targetFileName, int dwFlags);

        public static bool CreateSymbolicLink(string symlinkFileName, string targetFileName)
        {
            return CreateSymbolicLink(symlinkFileName, targetFileName, SYMLINK_FLAG_DIRECTORY);
        }

        [DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode,
            SetLastError = true)]
        static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen,
                                                          int flags);

        [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
                                                       IntPtr SecurityAttributes, int dwCreationDisposition,
                                                       int dwFlagsAndAttributes, IntPtr hTemplateFile);
        
        public static bool IsDirectorySymlink(string dirPath)
        {
            DirectoryInfo symlink = new DirectoryInfo(dirPath);
            return (symlink.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
        }

        public static string GetSymbolicLinkTarget(string dirPath)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(dirPath);
            if (!IsDirectorySymlink(dirPath)) return directoryInfo.FullName;

            SafeFileHandle directoryHandle = CreateFile(directoryInfo.FullName, 0, 2, IntPtr.Zero,
                                                        CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
                                                        IntPtr.Zero);
            if (directoryHandle.IsInvalid)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            StringBuilder path = new StringBuilder(512);
            int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(), path, path.Capacity, 0);
            if (size < 0)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\"
            // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
            if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
                return path.ToString().Substring(4);
            
            return path.ToString();
        }
    }
}